<!DOCTYPE html>
<html>
  <head>
    <title>Data Visualization</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
  </head>
  <body>
    <svg width="600" height="500" id="mainsvg" class="svgs">
    </svg>
    <script>
      let width = 600;
      let height = 500;
        d3.select("svg")
    .attr("width", width)
    .attr("height", height);

    let sdpi = 3;
    let r = 3;
    let up = "#ff5050";
    let down = "#26d25d";
    let notsig = "rgb(101 179 254)";
    var color = {
            green: down,
            red: up,
            blue: notsig,
            black:"orange",
            // "grap":"rgb(229, 229, 229)"
        }
        var round = {
            green:r,
            red:r,
            blue:r,
            black:r+4,
        }
        //图例数组，格式可自定义
        var data_legend = [
            {
                "name":"Down",
                "color":down,
            },
            {
                "name":"Up",
                "color":up,
            },
            {
                "name":"NotSig",
                "color":notsig,
            },
        ];





    let xAxisLabel1 = 'Log[2]FC';
    let yAxisLabel1 = '-Log[10]PValue';
    let titlePosition = "0"; //0:left ; 1:middle; 2:right
    let titleSize = "16px";
    let titleFamily = "Arial";
    let titleWidth = "0";
    let titleText = "VolcanoPlot";
    let xFontSize = '14px';
    let yFontSize = '14px';
    let yFontWeight = '0';
    let xFontWeight = '0';
    let xScaleNumber = '5';
    let yScaleNumber = '5';

    let xangle = 0;
    let yangle = 0;
    let yfonttop = 5;
    let xfonttop = 5;



    let xLabelSize = '18px';
    let yLabelSize = '18px';
    let xLabelplot = 'middle';
    let yLabelplot = 'middle';
    let xLabelFamily = 'Arial';
    let yLabelFamily = 'Arial';
    let xLabelWidth = '0';
    let yLabelWidth = '0';
    let fc = '1.5';
    let pvalue = '0.05';
    let oldfc = '1.5';
    let oldpvalue = '0.05';

    let gridline = "0"; //0: 无网格; 1: 有网格
    let legendSize = '16px';
    let legendheight = 10 ;
    let legendwidth  = 30;
    let legendPosition = "0"; //0:右方; 1: 下方; 2: 左方; 3: 上方
    let legendFamily = "Arial";

    let allFontFamily = "Arial";

    let shape = "circle";
    let shapeRound = 4;

    let infoFamily = "Arial";
    let infoSize = "16px";
    var arr =[];

    const svg = d3.select('svg');
    const margin = {top: 60, right: 100, bottom: 80, left: 100};
    let innerWidth = width - margin.left - margin.right;
    let innerHeight = height - margin.top - margin.bottom;
    let xScale, yScale;
    let xAxisLabel = xAxisLabel1;
    let yAxisLabel = yAxisLabel1;
    let xValue = d => Math.log2(d['FC']+0);
    let yValue = d => Math.abs(Math.log10(d['PValue'] + 0)); 
    let xLabelPosition = innerWidth/2;
    let yLabelPosition = -innerHeight/2.5;
    let xgridtheme = 0;
    let ygridtheme = 0;
    let titlesite = 0;
    if(gridline == "1"){
        console.log("网格线设置");
        xgridtheme = -innerHeight;
        ygridtheme = -innerWidth;
    }
    if(xLabelplot=='left'){
        xLabelPosition = '30px'
    }else if(xLabelplot == 'right'){
        xLabelPosition = innerWidth-30
    }
    if(yLabelplot=='top'){
        yLabelPosition = '0'
    }else if(yLabelplot == 'bottom'){
        yLabelPosition = -innerHeight+90
    }

    //标题位置设置
    if(titlePosition == "0"){
        titlesite = innerWidth/3;
    }else if(titlePosition == "1"){
        titlesite = innerWidth/1.3;
    }else{
        titlesite = innerWidth/1;
    }


    //初始化图例，将data_legend与图例绑定
    var legend = svg.selectAll(".legend") 
        .data(data_legend)
        .enter().append("g")
        .attr("class", "legend")
        .attr("transform", function(d, i) {
            if(legendPosition == 0){
                return "translate(-40," + (i * 20 + innerHeight/1.7) + ")"; 
            }else if(legendPosition == 3){
                return "translate("+(-i*legendSize*shapeRound-innerWidth/2) + ", 0)"; 
            }else if(legendPosition == 1){
                return "translate("+(-i*legendSize*shapeRound-innerWidth/2) + ","+ (innerHeight+110)+")"; 
            }else if(legendPosition == 2){
                return "translate("+(-innerHeight-120)+"," + (i * 20 + 200) + ")"; 
            }
            
            });  //transform属性便是整个图例的坐标

    //绘制文字后方的颜色框或线
    legend.append("circle")
        .attr("cx", width - 40) 
        .attr("cy", 12)
        .attr('r',shapeRound)
        .style("fill", function(d){
            return d.color
        });

    //绘制图例文字
    legend.append("text")
        .attr("x", width-30)
        .attr("y", 18)
        .attr('font-family',legendFamily)
        .attr("font-size",legendSize)
        .style("text-align", "left")
        .text(function(d) { 
            return d.name;
        });

    //初始化散点图
    const renderinit = function(data){
        // Linear Scale: Data Space -> Screen Space; 
        xScale = d3.scaleLinear()
        .domain([-d3.max(data, xValue), d3.max(data, xValue)]) // "extent" is equivalent to ; 
        .range([0, innerWidth]);

        // Introducing y-Scale; 
        yScale = d3.scaleLinear()
        .domain([0,d3.max(data, yValue)].reverse()) // remember to use reverse() to make y-axis start from the bottom; 
        .range([0, innerHeight]);



        // 添加标题文本到SVG
        svg.append("text")
        .attr("class", "chart-title")
        .attr("x", titlesite)
        .attr('font-size', titleSize)
        .attr('font-family',titleFamily)
        .attr('font-weight',titleWidth)
        .attr("y", 38)
        .attr("text-anchor", "middle")
        .text(titleText);

        // The reason of using group is that nothing is rendered outside svg, so margin of svg is always blank while margin of group is rendered inside svg; 
        //绘制坐标轴
        const g = svg.append('g').attr('id', 'maingroup')
        .attr('transform', `translate(${margin.left}, ${margin.top})`)
        .attr('font-size', '20px')

        //绘制网格线
        const g1 = svg.append('g').attr('id', 'maingroup1')
        .attr('transform', `translate(${margin.left}, ${margin.top})`)
        .attr('stroke-opacity','0.07')

        const xAxis1 =  d3.axisBottom(xScale).ticks(xScaleNumber).tickSize(xgridtheme);
        g1.append('g').call(xAxis1).attr('transform', `translate(${0}, ${innerHeight})`).style('font-size', '0')

        const yAxis1 =  d3.axisLeft(yScale).ticks(yScaleNumber).tickSize(ygridtheme);
        g1.append('g').call(yAxis1).style('font-size', '0')
        

        const xAxis = d3.axisBottom(xScale).ticks(xScaleNumber).tickSize(xgridtheme).tickSizeInner([5]).tickPadding(xfonttop);

        // xAxis.attr("stroke", "#d4d4d4");
    let xgroup = g.append('g').call(xAxis).attr('transform', `translate(${0}, ${innerHeight})`)
    .style('font-size', xFontSize)
    .style('font-weight',xFontWeight)
    .attr('font-family', allFontFamily)

    xgroup.append('text')
        .attr('font-size', xLabelSize)
        .attr('font-family',xLabelFamily)
        .attr('font-weight',xLabelWidth)
        .attr('y', 46)
        .attr('x', xLabelPosition)
        .attr('fill', '#333333')
        .attr('transform', `rotate(0)`)
        .text(xAxisLabel);

        if (xangle !== 0) {
        xgroup.selectAll('.tick text')
            .attr('transform', `rotate(${xangle})`)
            .style('text-anchor', 'end');
        }

        const yAxis = d3.axisLeft(yScale).ticks(yScaleNumber).tickSize(ygridtheme).tickSizeInner([5]).tickPadding(yfonttop);
        let ygroup = g.append('g').call(yAxis)
        .attr('font-size', yFontSize)
        .attr('font-weight',yFontWeight)
        .attr('font-family', allFontFamily)


        ygroup.append('text')
        .attr('font-size', yLabelSize)
        .attr('font-family',yLabelFamily)
        .attr('font-weight',yLabelWidth)
        .attr('transform', `rotate(-90)`)
        .attr('x', yLabelPosition)
        .attr('y', -60)
        .attr('fill', '#333333')
        .text(yAxisLabel)

        if (yangle !== 0) {
            ygroup.selectAll('.tick text')
                .attr('transform', `rotate(${yangle})`)
                .style('text-anchor', 'end');
        }

        const yaveline = yScale(Math.abs(Math.log10(pvalue)))
        //水平辅助线
        const xline = svg.append("line")
        .attr("x1", 0)
        .attr("x2", width-200)
        .attr("y1", yaveline)
        .attr("y2", yaveline)
        .attr("stroke-width", 1.5)
        .attr("stroke", "black")
        .attr("stroke-dasharray", "5,5")
        .attr('transform', `translate(${margin.left}, ${margin.top})`);
        //垂直辅助线
        const lxline = xScale(Math.log2(1/fc));
        const rxline = xScale(Math.log2(fc));
        console.log("lxline"+lxline);
        //垂直辅助线
        const ylline = svg.append("line")
        .attr("x1", lxline)
        .attr("x2", lxline)
        .attr("y1", 0)
        .attr("y2", height-130)
        .attr("stroke-width", 1.5)
        .attr("stroke", "black")
        .attr("stroke-dasharray", "5,5")
        .attr('transform', `translate(${margin.left}, ${margin.top})`);

        const yrline = svg.append("line")
        .attr("x1", rxline)
        .attr("x2", rxline)
        .attr("y1", 0)
        .attr("y2", height-130)
        .attr("stroke-width", 1.5)
        .attr("stroke", "black")
        .attr("stroke-dasharray", "5,5")
        .attr('transform', `translate(${margin.left}, ${margin.top})`);
    };


    const renderUpdate = function(data){
        let tooltip = d3.select('body')
        .append('div')
        .style('position', 'absolute')
        .style('z-index', '10')
        .style('color', '#fff')
        .style('background','black')
        .style('width','100px')
        .style('height','60px')
        .style('text-align','center')
        .style('padding-top','5px')
        .style('visibility', 'visible')   // 是否可见（一开始设置为隐藏）
        .style('font-size', '12px')
        .style('font-weight', 'bold')
        .text('');

        const clickarr = [];
        let infoBoxStartPos = {};
        const g = d3.select("#maingroup");
        let circleupdates = g.selectAll('circle').data(data,d => d['Name']);
        let circleenter =circleupdates.enter().append('circle')
        .attr('cx', d => xScale(xValue(d)))
        .attr('cy', d => yScale(yValue(d)))
        .attr('r',d=> round[d['group']])
        .attr('fill',d=> color[d['group']])
        .attr('opacity',0.6)

        .on('click',function(d,i){
            if(arr.indexOf(d.Name)==-1){
                arr.push(d.Name)
                const dd=d;
                const infoBox = svg.append("text")
                            .attr("x", xScale(xValue(d))+60)
                            .attr("y", yScale(yValue(d))+42)
                            .style("fill", "#000")
                            .style("font-family",infoFamily)
                            .style('font-size',infoSize)
                            .text(d.Name);
                                        
                    const lines = svg.append("line")
                        .attr("x1", xScale(xValue(d))+80)
                        .attr("y1", yScale(yValue(d))+42)
                        .attr("x2", xScale(xValue(d))+100)
                        .attr("y2", yScale(yValue(d))+60)
                        .attr("stroke-width", 1)
                        .attr("stroke", "black");

                    const drag = d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged);

                    infoBox.call(drag);

                    function dragstarted(event, d) {
                      d3.select(this).raise();
                      infoBoxStartPos = { x: d3.event.x, y: d3.event.y };
                    }

                    function dragged(event, d) {

                    infoBox.attr("x", d3.event.x)
                            .attr("y", d3.event.y);

                    lines.attr("x1", +infoBox.attr("x") + 20)
                        .attr("y1", +infoBox.attr("y") + 6)
                        .attr("x2", xScale(xValue(dd))+100)
                        .attr("y2", yScale(yValue(dd))+60);
                    }
            }

        // 显示当前点击点的信息框
            if(!arr.includes(d)){
                arr.push(d);
                d3.select(this)
                // .attr('fill','black')
                .attr('r',r+4)
                .attr('opacity',1) 
                emit('selectarr',JSON.stringify(arr))
                
            }else{
                arr.splice(arr.indexOf(d),1)
                d3.select(this)
                .attr('r',r)
                .attr('opacity',0.6)  
                emit('selectarr',JSON.stringify(arr))
            }

        })
        .on('mouseover', function (d, i) {
            d3.select(this)
            .attr('opacity',1)
            .attr('stroke', d=> color[d['group']])
            .attr('stroke-width','5px')
            .attr('stroke-opacity','0.6')
            .attr('cursor','pointer')

            return tooltip.style('visibility', 'visible')
            .text( "Name:"+d.Name+"  "+"FC:"+d.FC.toFixed(2)+"  "+"PValue:"+ d.PValue.toExponential(2))
        })
        .on('mousemove', function (d, i) {
            d3.select(this)
            .attr('stroke', d=> color[d['group']])
            .attr('stroke-width','5px')
            .attr('stroke-opacity','0.6')
            .attr('opacity',1)
            return tooltip.style('top', (event.pageY-10)+'px').style('left',(event.pageX+10)+'px')
        })
        .on('mouseout', function (d, i) {
            d3.select(this)
            .attr('opacity',0.6)
            .attr('stroke-width','0')
            return tooltip.style('visibility', 'hidden')
        });

    
    //套索

    }
        const data = [
          {Name:"id1",FC:"0.1",PValue:"0.0078",Maker:"0"},
          {Name:"id1",FC:"0.06",PValue:"0.00129",Maker:"0"},
          {Name:"id1",FC:"0.3",PValue:"1.3",Maker:"0"},
          {Name:"id1",FC:"17",PValue:"0.00497",Maker:"0"},
          {Name:"id1",FC:"0.5",PValue:"0.0099",Maker:"0"},
          {Name:"id1",FC:"7",PValue:"0.0013",Maker:"0"},
          {Name:"id1",FC:"10",PValue:"0.0079",Maker:"0"},
          {Name:"id1",FC:"0.8",PValue:"3.0",Maker:"0"},
          {Name:"id1",FC:"0.9",PValue:"2.0",Maker:"0"}, 
        ]
        data.forEach( d => {
            d['FC'] =+ (d['FC']);
            d['PValue'] =+ (d['PValue']);
            d['Marker'] =+ (d['Marker']);
        });
        if(data.some(item=>item.hasOwnProperty('group'))){
            console.log(pvalue);
            console.log(fc);
            for (let i = 0; i < data.length; i++) {
                const e = data[i];
                if(e.group=="white" || oldfc != fc || oldpvalue != pvalue){
                    if(e.PValue > pvalue){
                        e.group="blue";
                    }else{
                        if(e.FC<1/fc){
                            e.group="green"
                        }else if(e.FC>fc){
                            e.group="red"
                        }else{
                            e.group="blue"
                        }
                    }
                }
            }
            console.log("更改数据");

        }else{
            console.log("添加颜色");
            console.log(pvalue);
            console.log(fc);
        
            for (let i = 0; i < data.length; i++) {
            const ele = data[i];

            if(ele.PValue>pvalue){
                ele.group="blue";
            }else{
                if(ele.FC<1/fc){
                    ele.group="green"
                }else if(ele.FC>fc){
                    ele.group="red"
                }else{
                    ele.group="blue"
                }
            }
            }
        }
        for (let i = 0; i < data.length; i++) {
            const e = data[i];
            if(e.PValue==0){
                data.splice(i,1)
            }
            
        }  

        renderinit(data);
        renderUpdate(data);
 

    </script>
  </body>
</html>